#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sys/stat.h>
#include <iomanip>
#include <cstring>
#include <unistd.h>
#include <cassert>
#include <dirent.h>
#include <vector>
#include <typeinfo>
#include <pthread.h>
#include <algorithm>

using namespace std;

// options
enum OPTION {
    OPT_h,
    OPT_A,
    OPT_b,
    OPT_r,
    OPT_s,
    OPT_t,
    OPT_NUM
};

// gobal variable
int g_option[OPT_NUM];
vector <string> g_dirPath;
int dirFd; // the file descriptor  associated with the directory
// 线程共享资源
int **line_printed;
// 线程共享资源
string *path_printed;
// 线程共享资源
int counter;
// 限定的文件类型（cpp/txt etc.）（针对-s --suffix）
string suffix;
// 用户输入的线程数目
int thread_num;
// 存储所有需要计算行数的文件路径，线程共享资源
vector<const char *> files;
// 是否往files中添加了文件
bool whether_add_file;
// 互斥量
pthread_mutex_t mtx_1;
pthread_mutex_t mtx_2;

bool is_s;
bool is_t;

/** 声明 */
// 线程调用函数
void *Run(void *arg);

/**
 * 判断输入的字符串是不是文件（0），文件夹（1），mode（2），都不是则为3
 */
int is_file(const char *pathname);

int *compute_line_number(const char *pathname);

void print(int mode);

void print_help();

void get_option(const char *strOption);

void do_lc(const char *path);

void get_path(const char *strPath);
// vector<string> get_files(const char *path);

int main(int argc, char *argv[]) {
    // global initialize
    counter = 0;
    suffix = "";
    line_printed = new int *[100];
    path_printed = new string[100];
    whether_add_file = false;
    is_s = false;
    is_t = false;

    memset(g_option, 0, sizeof(int) * OPT_NUM);
    // 传入的参数数量
    int arg_count = argc - 1;

    // process option
    while (--argc != 0) {
        if ('-' == argv[argc][0]) {
            get_option(argv[argc]);
        } else {
            get_path(argv[argc]);
        }
    }

    // -s 特殊操作
    if (is_s == true) {
        // 确定文件类型
        char *file_type = new char[1];
        for (int i = 1; i <= arg_count; i++) {
            if (strcmp(argv[i], "-s") == 0) {
                file_type = argv[i + 1];
                break;
            }
        }

        if (file_type[0] == '.') {
            suffix = file_type;
        } else {
            string temp = file_type;
            suffix = "." + temp;
        }

        // 删除操作
        int del_id = -1;
        for (int i = 0; i < g_dirPath.size(); i++) {
            /* code */
            if (strcmp(g_dirPath[i].data(), file_type) == 0) {
                del_id = i;
                break;
            }
        }
        auto it = g_dirPath.begin();
        g_dirPath.erase(it + del_id);
    }

    if (is_t == true) {
        string num_str = "";
        for (int i = 1; i <= arg_count; i++) {
            if (strcmp(argv[i], "-t") == 0) {
                num_str = argv[i + 1];
                thread_num = atoi(num_str.c_str());
                break;
            }
        }

        // 删除操作
        int del_id = -1;
        for (int i = 0; i < g_dirPath.size(); i++) {
            /* code */
            if (strcmp(g_dirPath[i].data(), num_str.data()) == 0) {
                del_id = i;
                break;
            }
        }
        auto it = g_dirPath.begin();
        g_dirPath.erase(it + del_id);
    }

    if (g_dirPath.empty()) {
        // 打印help
        print(2);
    } else {
        pthread_mutex_lock(&mtx_1);
        for (auto it = g_dirPath.begin(); it != g_dirPath.end();) {
            // cout<<"line:72  "<<it->c_str()<<endl;
            do_lc(it->c_str());
            ++it;
        }
        pthread_mutex_unlock(&mtx_1);
    }

    if (g_option[OPT_t] == 1 && thread_num > 0) {
        // 创建线程
        int file_num = files.size();
        // 为避免线程执行顺序的混乱，加个额外限制避免影响到这部分
        if (file_num == 0) {
            file_num = 1;
        }
        // 线程数须小于等于待读取文件数
        if (thread_num > file_num) {
            thread_num = file_num;
        }
        pthread_t threads[thread_num];

        for (int i = 0; i < thread_num; i++) {
            /* code */
            int err = pthread_create(&threads[i], NULL, &Run, NULL);
            if (err != 0) {
                cout << "cna't create thread1" << endl;
                exit(-1);
            }
        }
    }

    // 打印
    if (g_option[OPT_t] == 1) {
        int len = files.size();
        while (true) {
            if (counter == len && whether_add_file) {
                print(2);
                break;
            }
        }
    } else {
        print(2);
    }

    pthread_exit(NULL);
    return 0;
}

void *Run(void *arg) {
    // pthread_t tid = pthread_self();
    while (files.size() > 0 && whether_add_file) {
        const char *path;
        pthread_mutex_lock(&mtx_1);
        if (files.size() > 0) {
            path = files.back();
            files.pop_back();
        } else {
            break;
        }
        pthread_mutex_unlock(&mtx_1);

        // 写共享资源

        int *lines = new int[3];
        lines = compute_line_number(path);

        pthread_mutex_lock(&mtx_2);
        line_printed[counter] = lines;
        path_printed[counter] = path;
        counter++;
        pthread_mutex_unlock(&mtx_2);

        // 挂起0.1秒
        usleep(100000);
    }

    pthread_exit(NULL);
}

int is_file(const char *pathname) {
    struct stat buf;
    int result;
    result = stat(pathname, &buf);
    if (S_IFDIR & buf.st_mode) {
        // 文件夹
        // cout<<"folder"<<endl;
        return 1;
    } else if (S_IFREG & buf.st_mode) {
        // 文件
        return 0;
    } else {
        int a = 0;
        return 2;
    }
}

int *compute_line_number(const char *pathname) {
    int *arr = new int[3];

    ifstream infile(pathname);
    char str[1000];
    // 空行计数
    int null_count = 0;
    // 非空行计数
    int content_count = 0;
    // 总行数计数
    int total_count = 0;

    if (infile.is_open())
        while (!infile.eof()) {
            infile.getline(str, sizeof(str));
            if (str != nullptr && (str[0] == '\r' || str[0] == '\n' || (int) (str[0]) == 0)) {
                // 为空(对应ASCII码为13,10,0)
                null_count++;
            } else {
                // 不为空
                content_count++;
            }
            total_count = content_count + null_count;
        }
    // cout << "The null count is " << null_count << endl;
    // cout << "The content count is " << content_count << endl;
    // cout << "The total line number is: " << total_count << endl;

    arr[0] = content_count;
    arr[1] = null_count;
    arr[2] = total_count;

    return arr;
}

void print(int mode) {
    if (g_option[OPT_b] == 1) {
        // -b 忽略空行
        mode = 0;
    } else if (mode < 0 && mode > 2) {
        mode = 2;
    }

    if (g_option[OPT_h] || counter == 0) {
        print_help();
    } else {
        cout << left << setw(10) << "line"
             << "file" << endl;
        cout << left << setw(10) << "----"
             << "----" << endl;

        // 计算总和
        int sum = 0;
        for (int i = 0; i < counter; i++) {
            if (g_option[OPT_s] && path_printed[i].find(suffix) > path_printed[i].length())
                continue;

            sum += line_printed[i][mode];
        }
        if (g_option[OPT_A] && !(counter == 1 && is_file(path_printed[0].data()) == 0)) {
            // -A --all
            cout << right << setw(4) << sum << "      "
                 << "total" << endl;
        } else {

            // 正常模式
            for (int i = 0; i < counter; i++) {
                if ((g_option[OPT_s] == 1) && path_printed[i].find(suffix) > path_printed[i].length()) {
                    continue;
                }
                cout << right << setw(4) << line_printed[i][mode] << "      " << path_printed[i] << endl;
            }
            if (counter > 1)
                cout << right << setw(4) << sum << "      "
                     << "total" << endl;
        }

        cout << endl;
    }
}

void print_help() {
    int blank = 30;
    cout << "Usage: lc [OPTION] ... [FILE | DIR] ..." << endl;
    cout << "Print newline counts for each FILE or DIR, and a total line if more than one FILE is specified." << endl
         << endl;
    cout << "With no FILE, or when FILE is -, read standard input." << endl
         << endl;
    cout << "The options below may be used to control print msg" << endl;
    cout << setw(5) << "" << left << setw(blank) << "-A, --all"
         << "print the total line number only" << endl;
    cout << setw(5) << "" << left << setw(blank) << "-b"
         << "ignore the Blank line when counting" << endl;
    cout << setw(5) << "" << left << setw(blank) << "-r"
         << "count directory Recursively" << endl;
    cout << setw(5) << "" << left << setw(blank) << "-s, --suffix=FILE-SUFFIX"
         << "only count the file with specified Suffix" << endl;
    cout << setw(5) << "" << left << setw(blank) << "-t, --thread=NUMBER"
         << "use multithread to count" << endl;
    cout << setw(5) << "" << left << setw(blank) << "-h, --help"
         << "display this Help and exit" << endl;
    exit(0);
}

void get_option(const char *strOption) {
    assert(strOption);
    if (strcmp(strOption, "-h") == 0 || strcmp(strOption, "--help") == 0) {
        g_option[OPT_h] = 1;
    } else if (strcmp(strOption, "-A") == 0 || strcmp(strOption, "--all") == 0) {
        g_option[OPT_A] = 1;
    } else if (strcmp(strOption, "-b") == 0) {
        g_option[OPT_b] = 1;
    } else if (strcmp(strOption, "-r") == 0) {
        g_option[OPT_r] = 1;
    } else if (strcmp(strOption, "-s") == 0 && g_option[OPT_s] == 0) {
        g_option[OPT_s] = 1;
        is_s = true;
    } else if (((string) strOption).find("--suffix") == 0 && g_option[OPT_s] == 0) {
        string temp = strOption;
        if (temp.length() <= 9) {
            print_help();
        }
        suffix = "." + temp.substr(9, temp.length() - 9);
        g_option[OPT_s] = 1;
    } else if (strcmp(strOption, "-t") == 0 && g_option[OPT_t] == 0) {
        g_option[OPT_t] = 1;
        is_t = true;
    } else if (((string) strOption).find("--thread") == 0 && g_option[OPT_t] == 0) {
        string temp = strOption;
        if (temp.length() <= 9) {
            print_help();
        }
        thread_num = atoi(temp.substr(9, temp.length() - 9).c_str());
        g_option[OPT_t] = 1;
    } else {
        print_help();
    }
}

void get_path(const char *strPath) {
    assert(strPath);
    g_dirPath.push_back(strPath);
}

void do_lc(const char *path) {

    assert(path);

    DIR *p_dir;
    struct dirent *p_next;
    vector <string> list;
    // 0: file; 1:folder;
    int flag = is_file(path);
    if (flag == 0) {
        // // 文件
        // int *lines = new int[3];
        // lines = compute_line_number(path);
        // string pathName = path;
        // // print(lines, pathName, 0);

        // line_printed[counter] = lines;
        // path_printed[counter] = pathName;
        // counter++;
        // 将要计算的文件名添加到vector中，等最终用线程进行计算
        if (g_option[OPT_t] == 1) {
            whether_add_file = true;
            files.push_back(path);
        } else {
            int *lines = new int[3];
            lines = compute_line_number(path);
            string pathName = path;
            // print(lines, pathName, 0);

            line_printed[counter] = lines;
            path_printed[counter] = pathName;
            counter++;
        }
    } else if (flag == 1) {
        // 文件夹
        if ((p_dir = opendir(path)) == NULL) {
            perror("opendir error");
            exit(1);
        }

        if ((dirFd = dirfd(p_dir)) == -1) {
            perror("dirfd error");
            exit(0);
        }

        while ((p_next = readdir(p_dir)) != NULL) {
            // cout << "p_next->d_name: " << p_next->d_name << endl;
            // process option -a
            string temp = path;
            string connector = "/";
            if (temp.back() == '/')
                connector = "";
            string pathname = temp + connector + p_next->d_name;

            if (is_file(pathname.data()) == 1) {
                // 当不采用-r模式时，所有文件夹都跳过
                // 当采用-r模式时，除了"."和".."之外的文件夹都跳过
                if (g_option[OPT_r] == 0 || (strcmp(p_next->d_name, ".") == 0 || strcmp(p_next->d_name, "..") == 0)) {
                    continue;
                }
            }
            list.push_back(pathname);
        }
        for (int i = 0; i < list.size(); i++) {
            do_lc(list[i].data());
        }
    }
}

// #include <pthread.h>
// #include <iostream>
// #include <unistd.h>
// #include <vector>
// #include <fstream>
// #include <stdio.h>
// #include <stdlib.h>
// #include <fstream>
// #include <sys/stat.h>
// #include <iomanip>
// #include <cstring>
// #include <assert.h>
// #include <dirent.h>
// #include <vector>
// #include <typeinfo>

// using namespace std;

// int **line_printed;
// string *path_printed;
// int counter;
// vector<char *> files;
// pthread_mutex_t mtx_1;
// pthread_mutex_t mtx_2;

// /** 声明 */
// void *Run(void *arg);
// int *compute_line_number(const char *pathname,pthread_t tid);

// void *Run(void *arg)
// {
//     pthread_t tid = pthread_self();
//     while (files.size() > 0)
//     {
//         char *path;
//         pthread_mutex_lock(&mtx_1);
//         if (files.size() > 0)
//         {
//             path = files.back();
//             files.pop_back();
//         }
//         else
//         {
//             break;
//         }
//         pthread_mutex_unlock(&mtx_1);

//         // 写共享资源

//         int *lines = new int[3];
//         lines = compute_line_number(path,tid);

//         pthread_mutex_lock(&mtx_2);
//         line_printed[counter] = lines;
//         path_printed[counter] = path;
//         counter++;
//         pthread_mutex_unlock(&mtx_2);

//         sleep(1);
//     }

//     char *id = (char *)arg;
//     cout << id << endl;
//     pthread_exit(NULL);
// }

// int *compute_line_number(const char *pathname, pthread_t tid)
// {
//     int *arr = new int[3];

//     ifstream infile(pathname);
//     char str[1000];
//     // 空行计数
//     int null_count = 0;
//     // 非空行计数
//     int content_count = 0;
//     // 总行数计数
//     int total_count = 0;

//     if (infile.is_open())
//         while (!infile.eof())
//         {
//             infile.getline(str, sizeof(str));
//             if (str != nullptr && (str[0] == '\r' || str[0] == '\n' || (int)(str[0]) == 0))
//             {
//                 // 为空(对应ASCII码为13,10,0)
//                 null_count++;
//             }
//             else
//             {
//                 // 不为空
//                 content_count++;
//             }
//             total_count = content_count + null_count;
//         }
//     // cout << "The null count is " << null_count << endl;
//     // cout << "The content count is " << content_count << endl;
//     // cout << "The total line number is: " << total_count << endl;

//     arr[0] = content_count;
//     arr[1] = null_count;
//     arr[2] = total_count;
//     cout<<pathname<<": ("<<tid<<")"<<endl;
//     cout<<"content_count: "<<arr[0]<<"\nnull_count: "<<arr[1]<<"\ntotal_count: "<<arr[2]<<endl;
//     return arr;
// }

// void print(int mode)
// {

//     cout << left << setw(10) << "line"
//          << "file" << endl;
//     cout << left << setw(10) << "----"
//          << "----" << endl;

//     // 正常模式
//     for (int i = 0; i < counter; i++)
//     {
//         cout << right << setw(4) << line_printed[i][mode] << "      " << path_printed[i] << endl;
//     }

//     cout << endl;
// }

// int main()
// {
//     line_printed = new int *[100];
//     path_printed = new string[100];
//     cout << "main函数开始执行" << endl;
//     pthread_t threads[2];
//     char *path_1 = "/root/amer/user/aos10/experiment02/myTest/exe_folder/10_5.txt";
//     char *path_2 = "/root/amer/user/aos10/experiment02/myTest/exe_folder/cpp_proj/50_10.h";
//     char *path_3 = "/root/amer/user/aos10/experiment02/myTest/exe_folder/cpp_proj/80_25.cpp";
//     char *path_4 = "/root/amer/user/aos10/experiment02/myTest/exe_folder/cpp_proj/20_10.txt";
//     files.push_back(path_1);
//     files.push_back(path_2);
//     files.push_back(path_3);
//     files.push_back(path_4);
//     int len = files.size();

//     int err = pthread_create(&threads[0], NULL, &Run,NULL);
//     if (err != 0)
//     {
//         cout << "cna't create thread1" << endl;
//         exit(-1);
//     }
//     err = pthread_create(&threads[1], NULL, &Run, NULL);
//     if (err != 0)
//     {
//         cout << "cna't create thread2" << endl;
//         exit(-1);
//     }

//     cout << "\nmain函数结束" << endl;
//     /* main() 显示地调用了 pthread_exit() 来等待其他线程的结束。
//     （如果不使用这个函数的话，可能main()函数结束了也有线程没有执行完毕！） */
//     cout<<threads[0]<<endl;
//     cout<<threads[1]<<endl;
//     while(true){
//         if (counter == len){
//             print(2);
//             break;
//         }
//     }
//     pthread_exit(NULL);
// }